:-dynamic(barco/4).
%barco(Ci,Fi,dir,tam).
%barco(0,2,h,3).
%barco(3,0,v,2).
%barco(0,0,h,1).
%Montoya
%barco(1,0,v,3).
%barco(3,0,h,2).
%barco(3,2,h,1).
%otro ejemplo
%barco(0,0,h,2).
%barco(3,0,h,2).
%barco(1,1,v,2).
%barco(3,1,v,3).
%barco(4,1,v,1).
%barco(4,2,v,1).
%barco(0,3,h,1).
%barco(1,3,h,1).
%barco(4,3,v,2).
%barco(1,4,h,3).
%barco(1,5,h,4).
%barco(0,6,h,5).
/*-------------------------------------------------
Proyecto II: Batalla Naval (Version Solitario)
Autores: Dayana de Ponte, 06-39435
         Rafael Barretta, 06-39206

  En esta version se debe proveer las posiciones de
los barcos, y su solucion debe generar una secuencia 
de jugadas que conlleven a hundir todos los barcos. 

---------------------------------------------------*/

%% jugar
%  Se satisface una vez se ha obtenido las dimensiones del tablero, 
%  el numero de balas, informacion de los barcos, se han mostrado 
%  paso a paso cada uno de los movimientos realizados por el programa 
%  hasta conseguir hundir todos los barcos colocados con el numero de 
%  balas disponibles.

jugar:- pedir_datos(F,C), 
        tablero_inicial(F,C,T0),
        ataque(T0,_,F,C).

%% pedir_datos(-Filas, -Columnas)
%  Obtiene informacion del tablero, numero de barcos.

pedir_datos(F, C):- write('Num. de Filas: '), read(F),
                    F > 0, 
                    F =< 20,
                    write('Num. de Columnas: '), read(C),
                    C > 0, 
                    C =< 20,
                    write('Cant. de Barcos: '), read(Nb),
                    solicitar_datos_barco(F,C,Nb),!.

%% solicitar_datos_barco(+Filas, +Columnas, +Acumulador).
%  Obtiene informacion de cada barco.

solicitar_datos_barco(_, _, 0).
solicitar_datos_barco(F, C, A):- A > 0,
				 write('Informacion de Barco: '),nl,
                                 write('Tamano: '),read(T), T>0,
                                 write('Direccion: '), read(D), es_v_o_h(D),
                                 write('Fila Inicial: '), read(Fi),
                                 write('Columna Inicial: '), read(Ci),
                                 cabe_en_tablero(Ci,Fi,D,T,C,F),
                                 assert(barco(Ci,Fi,D,T)),
                                 A1 is A-1, 
                                 solicitar_datos_barco(F,C,A1),!.
%% es_v_o_h(+Dir).
%  Establece:
%  - Una direccion horizontal viene dada por h.
%  - Una direccion vertical viene dada por v.

es_v_o_h(v).
es_v_o_h(h).

%% cabe_en_tablero(+Columna_Inicial, +Fila_Inicial, +Dir, +Tam, +Columna, +Fila).
%  Verifica que los datos de entrada de los barcos sean correctos.

cabe_en_tablero(_, Y0, v, T, _, Y):- Y1 is Y0+T, 
                                     Y1 =< Y.
cabe_en_tablero(X0, _, h, T, X, _):- X1 is X0+T, 
                                     X1 =< X.

%% tablero_inicial(+Filas, +Columnas, -Tablero).
%  Se satisface si Tablero de F filas, C columnas
%  posee todas sus posiciones con agua. 

tablero_inicial(0, _, []):- !.
tablero_inicial(F, C, T):- dibujar_fila(C,a,LF), 
                           F1 is F-1, 
                           tablero_inicial(F1,C,X), 
                           append(X,[LF],T).

%% dibujar_fila(+Columnas, +Elemento, -Fila)
%  Retorna una fila de tantos Elementos como Columnas haya.

dibujar_fila(0, _, []):- !.
dibujar_fila(C, E, F):- C1 is C-1, 
                        dibujar_fila(C1,E,X),
                        append(X,[E],F).

%% mostrar_tablero(+Tablero).
%  Dibuja un tablero en pantalla segun indique T.

mostrar_tablero([]):- write(''),!.
mostrar_tablero([H|R]):- mostrar_elemento(H),nl,
                         mostrar_tablero(R).

%% mostrar_elemento(+Tablero).
%  Dibuja elemento por elemento, por fila, segun 
%  lo que posee T.

mostrar_elemento([]):- write(''),!.
mostrar_elemento([H|R]):- write(H), write(' '),
                          mostrar_elemento(R).

%% estadoFinal(+Tablero).
%  Se satisface si en T se han hundido todos los barcos 
%  originalmente ocultos.
%  [Nota 1] Se obtiene una sublista para conocer el numero 
%           de filas que posee el tablero.
%  [Nota 2] Se determina que se ha llegado a un estado final
%           cuando el numero de casillas hundidas en el tablero
%           es igual al numero de casilla ocupadas por todos 
%           los barcos.

estado_final(T):- length(T,F),
                  obtener_sublista(T,0,Fila),            %ver [Nota 1] arriba.
                  length(Fila,C),
                  total_posiciones_barcos(C,F,N),
                  total_h(C,F,T,N2), 
                  N = N2,!.                              %ver [Nota 2] arriba.

%% total_posiciones_barcos(+Columnas, +Filas, -Numero)
%  Retorna en N el numero de casillas que ocupan todos los 
%  barcos, esten o no hundidos.

total_posiciones_barcos(C, F, N):- total_posiciones_barcos(C,F,0,0,0,N),!.

% total_posiciones_barcos(+Columnas, +Filas, +Coordenada_X, 
%                         +Coordenada_Y, +Inicial, -N).

total_posiciones_barcos(C, F, X, Y, N, N):- X < C,       %Caso base
                                            Y >= F,!.
total_posiciones_barcos(C, F, X, Y, I, N):- X < C,
                                            Y < F,
                                            hay_barco(X,Y),
                                            X1 is X+1,   %Nos movemos por las columas
                                            Ni is I+1,   %Sumamos una casilla
                                            total_posiciones_barcos(C,F,X1,Y,Ni,N),!.
total_posiciones_barcos(C, F, X, Y, I, N):- X < C,
                                            Y < F,
                                            not(hay_barco(X,Y)),
                                            X1 is X+1,   %Nos movemos por las columnas
                                            total_posiciones_barcos(C,F,X1,Y,I,N),!.
total_posiciones_barcos(C, F, X, Y, I, N):- X >= C,
                                            Y < F,
                                            Y1 is Y+1,   %Nos movemos por las filas
                                            X1 is 0,     %Comenzamos desde la columna 0
                                            total_posiciones_barcos(C,F,X1,Y1,I,N),!.

%% hay_barco(+Coordenada_X, +Coordenada_Y)
%  Se satisface si en la coordenada (X,Y) hay 
%  una casilla ocupada por un barco.

hay_barco(X, Y):- barco(X,Y,_,_),!. 
hay_barco(X, Y):- barco(X,Yi,v,Tam),
                  Y >= Yi,
                  P is Y-Yi,
                  P < Tam,!.
hay_barco(X, Y):- barco(Xi,Y,h,Tam),
                  X >= Xi,
                  P is X-Xi,
                  P < Tam,!.

%% total_h(+Columnas, +Filas, +Tablero, -Numero)
%  Retorna el numero de casillas hundidas (h) 
%  que hay en el tablero.

total_h(C, F, T, N):- total_h(T,C,F,0,0,0,N),!.

% total_h(+Tablero, +Columnas, +Filas, +Coordenada_X, 
%         +Coordenada_Y, +Inicial, -Numero)

total_h(_, C, F, X, Y, N, N):- X < C,                    %Caso Base
                               Y >= F,!.
total_h(T, C, F, X, Y, I, N):- X < C,
                               Y < F,
                               hay_elemento(T,X,Y,h),
                               X1 is X+1,                %Nos movemos por las columnas
                               Ni is I+1,                %Sumamos una casilla
                               total_h(T,C,F,X1,Y,Ni,N),!.
total_h(T, C, F, X, Y, I, N):- X < C,
                               Y < F,
                               not(hay_elemento(T,X,Y,h)),
                               X1 is X+1,                %Nos movemos por las columnas
                               total_h(T,C,F,X1,Y,I,N),!.
total_h(T, C, F, X, Y, I, N):- X >= C,
                               Y < F,
                               Y1 is Y+1,                %Nos movemos por las filas
                               X1 is 0,                  %Comenzamos desde la columna 0
                               total_h(T,C,F,X1,Y1,I,N),!.

%% hay_elemento(+Tablero_Inicial, +Coordenada_X, 
%               +Coordenada_Y, +Elemento)
%  Se satisface si en la posicion dada por (X,Y) hay o 
%  no el elemento especificado (a,f,g,h).

hay_elemento(T0, X, Y, E):- obtener_sublista(T0,Y,Fila),
                            tiene_elemento_fila(X,Fila,E). 

%% obtener_sublista(+Lista, +Numero, -Lista_Final)
%  Retorna la fila o columna (segun sea el caso) de 
%  un tablero o fila, especificada por Numero.

obtener_sublista([H|_], 0, Lf):- Lf = H, !.
obtener_sublista([_|R], N, Lf):- N1 is N-1,
                                 obtener_sublista(R,N1,Lf).

%% tiene_elemento_fila(+Posicion, +Lista, +Elemento)
%  Se satisface si en la posicion dada por P hay el Elemento.

tiene_elemento_fila(0, [H|_], E):- E = H, !.
tiene_elemento_fila(P, [_|R], E):- P1 is P-1, 
                                   tiene_elemento_fila(P1,R,E).

%% ataque(+Tablero_Inicial, -Tablero, +Filas, +Columnas)
%  Retorna T1 que corresponde a realizar un disparo 
%  sobre el tablero T0.

ataque(T0, T1, F, C) :- write('Cant. de proyectiles disponibles: '), read(B), nl,
                        ataque_recursivo(T0,T1,F,C,0,0,B,[],Sol),
						mostrar_tablero(T0),nl,
						llenar_ataque_secuencial(T0,F,C,0,0),!.

%______________________________________________________________________________________________________________
%% ataque_recursivo(+Tablero_Inicial, -Tablero, +Filas, +Columnas, 
%                   +Coordenada_X, +Coordenada_Y, +Balas, +Lista, -Solucion)    
% Retorna tablero con ultima jugada ^ lista de tableros que
% representan las jugadas que hunden a todos los barcos.
% [Nota 1] Una vez que hundi a todos los barcos, no sigo 
%          disparando.


ataque_recursivo(T, T, F, C, X, Y, B, Sol, Sol):- B >= 0,              %Caso Base
                                                  X < C,
                                                  Y < F,
                                                  estado_final(T),!.   %Ver [Nota 1] arriba.
ataque_recursivo(T, T, F, C, X, Y, B, Sol, Sol):- B >= 0,               %Caso Base
                                                  X >= C,
                                                  Y < F,
                                                  estado_final(T),!.
ataque_recursivo(T0, T, F, C, X, Y, B, L, Sol):-  B = 0,
                                                  X < C,
                                                  Y < F,
                                                  not(estado_final(T0)),
                                                  devolver_tablero(T0,C,F,Y,T1),
                                                  length(L,N),
                                                  N1 is N-1,
                                                  obtener_inicio(L, N1, Sol1),
                                                  B1 is 1,
                                                  ataque_recursivo(T1,T,F,C,X,Y,B1,Sol1,Sol),!. %ESTE ES
ataque_recursivo(T0, T, F, C, X, Y, B, L, Sol):-  B = 0,
                                                  X >= C,
                                                  Y < F,
                                                  not(estado_final(T0)),
                                                  devolver_tablero(T0,C,F,Y,T1),
                                                  length(L,N),
                                                  N1 is N-1,
                                                  obtener_inicio(L, N1, Sol1),
                                                  B1 is 1,
                                                  Y1 is Y+1,
                                                  X1 is 0,
                                                  ataque_recursivo(T1,T,F,C,X1,Y1,B1,Sol1,Sol),!.  %ESTE ES
ataque_recursivo(T0, T, F, C, X, Y, B, L, Sol):-  B > 0,
                                                  X < C,
                                                  Y < F,
                                                  not(estado_final(T0)),
                                                  ataque(T0,T1,F,X,Y),
												  assert(ataque(X,Y)),
                                                  append(L,[T1],Sol1),
                                                  X1 is X+1,           %Nos movemos por las columnas
                                                  B1 is B-1,           %Disminuimos el num de balas
                                                  ataque_recursivo(T1,T,F,C,X1,Y,B1,Sol1,Sol),!.
ataque_recursivo(T0, T1, F, C, X, Y, B, L, Sol):- B > 0,
                                                  X >= C,
                                                  Y < F,
												  not(estado_final(T0)),
                                                  Y1 is Y+1,          %Nos movemos por las filas
                                                  X1 is 0,            %Comenzamos desde la columna 0
                                                  ataque_recursivo(T0,T1,F,C,X1,Y1,B,L,Sol).


devolver_tablero(T0, C, F, Acum, T):- Acum >= 0,
                                      obtener_sublista(T0,Acum,Fila),
                                      buscar_fallo_fila(C, Fila, X),
                                      X >= 0,                                 
                                      dibujar_ataque(T0, T, F, X, Acum, a),
									  retract(ataque(X,Acum)),!. %Cambiamos ultimo fallo por agua
devolver_tablero(T0, C, F, Acum, T):- Acum >= 0,
                                      obtener_sublista(T0,Acum,Fila),
                                      buscar_fallo_fila(C, Fila, X),
                                      X = -1,                            %No hay fallo en la fila                              
                                      Acum1 is Acum-1,
                                      devolver_tablero(T0,C,F,Acum1,T),!.

buscar_fallo_fila(0, _, -1).
buscar_fallo_fila(C, Fila, X):- C1 is C-1, 
                                tiene_elemento_fila(C1,Fila,f),
                                X is C1,!.
buscar_fallo_fila(C, Fila, X):- C1 is C-1, 
                                not(tiene_elemento_fila(C1,Fila,f)),
                                buscar_fallo_fila(C1,Fila,X),!.                                   
%______________________________________________________________________________________________________________
%% ataque(+Tablero_Inicial, -Tablero, +Filas, 
%         +Coordenada_X, +Coordenada_Y)   
%  Retorna tablero que representa un ataque sobre el tablero
%  inicial en la coordenada (X,Y).

ataque(T0, T1, F, X, Y):- not(hay_barco(X,Y)),
                             hay_elemento(T0,X,Y,a),                %Ataque a agua
                             dibujar_ataque(T0,T1,F,X,Y,f),!.
ataque(T0, T1, F, X, Y):- hay_barco(X,Y), 
                             dibujar_ataque(T0,T1,F,X,Y,g),       %Ataque a un barco no hundido
                             not(barco_hundido(T1,X,Y)),!.
ataque(T0, T1, F, X, Y):- hay_barco(X,Y),
                             dibujar_ataque(T0,T,F,X,Y,g),
                             barco_hundido(T,X,Y),                  %Indica si un barco se hunde
                             obtener_datos(X,Y,Ci,Fi,Dir,Tam),
                             hundir_barco(Ci,Fi,Dir,Tam,T,T1),!.    %Hunde el barco


%% dibujar_ataque(+Tablero_Inicial, -Tablero, +Filas, 
%                 +Coordenada_X, +Coordenada_Y, +Elemento)
% Retorna Tablero con el ataque dibujado.

dibujar_ataque(T0, T1, F, X, Y, E):- obtener_sublista(T0,Y,Fila),
                                     cambiar_edo(Fila,X,E,Fila_Cambiada),
                                     obtener_inicio(T0,Y,L1),
                                     append(L1,[Fila_Cambiada],L2),
                                     length(L2,Tam),
                                     Aux is F-Tam,
                                     obtener_fin(T0,Aux,L3),
                                     append(L2,L3,T1),!.

%% cambiarEdo(+Fila, +Posiciom, +Elemento, -Fila_Final)
%  Retorna una fila con, especificamente en la posicion P, un 
%  caracter especificado por Elemento.

cambiar_edo(F, P, E, Ff):- length(F,Tam), 
                           obtener_inicio(F,P,A),
                           append(A,[E],B),
                           C1 is Tam-P-1,
                           obtener_fin(F,C1,C),
                           append(B,C,Ff).

%% obtener_inicio(+Lista, +N, -Lista_Final)
%  Retorna los primeros N elementos de la Lista. 

obtener_inicio(_, 0, []):- !.
obtener_inicio([H|R], N, Lf):- X1 is N-1,
                               obtener_inicio(R,X1,A),
                               append([H],A,Lf).

%% obtener_fin(+Lista, +N, -Lista_Final)
%  Retorna los ultimos N elementos de la Lista. 
%  [Nota 1] Se crea una lista de tam N, con 
%           cualquier cosa.
%  [Nota 2] Cuando se hace el append, necesariamente 
%           Lf va a tener los ultimos N elementos de
%           la lista.

obtener_fin(L, N, Lf):- length(L,Tam),
                        N > Tam,
                        append([],[],Lf),!.
obtener_fin(L, N, Lf):- length(L,Tam),
                        N =< Tam,
                        length(Lf,N),                               %Ver [Nota 1] arriba.
                        append(_,Lf,L),!.                           %Ver [Nota 2] arriba.

%% barco_hundido(+Tablero_Inicial, +Coordenada_X,
%                +Coordenada_Y)
%  Se satisface si un barco se hunde con la coordenada
%  (X,Y).

barco_hundido(T0, X, Y):- obtener_datos(X,Y,X0,Y0,D,T),
                          recorrer_tablero(T0,X0,Y0,D,T,0).

%% obtener_datos(+Coordenada_X, +Coordenada_Y, -Columna_Inicial, 
%                -Fila_Inicial, -Direccion, -Tamano)
%  Retorna, Dado un (X,Y) cualquiera, los datos que corresponden 
%  al barco.

obtener_datos(X, Y, X, Y, Dir, Tam):- barco(X,Y,Dir,Tam),!. 
obtener_datos(X, Y, Ci, Fi, Dir, Tam):- barco(X,Y1,Dir,Tam),
                                        Dir=v,
                                        Ci is X,
                                        Fi is Y1,
                                        Y >= Y1,
                                        P is Y-Y1,
                                        P < Tam,!.
obtener_datos(X, Y, Ci, Fi, Dir, Tam):- barco(X1,Y,Dir,Tam),
                                        Dir=h,
                                        Ci is X1,
                                        Fi is Y,
                                        X >= X1,
                                        P is X-X1,
                                        P < Tam,!.

%% recorrer_tablero(+Tablero_Inicial, +Coordenada_Xc, +Coordenada_Y, 
%                   +Direccion, +Tamano, +Acumulador)
%  Esta funcion recorre un tablero, dado una posicion inicial de 
%  barco, para saber si un barco se hunde o no.

recorrer_tablero(_, _, _, Dir, Tam, Acum):- Dir=h, 
                                            Acum >= Tam,!.
recorrer_tablero(_, _, _, Dir, Tam, Acum):- Dir=v, 
                                            Acum >= Tam,!.
recorrer_tablero(T0, Xc, Y, Dir, Tam, Acum):- Dir=h,	
                                              Acum < Tam,
                                              Acum1 is Acum+1, 
                                              hay_elemento(T0,Xc,Y,g),
                                              X1 is Xc+1,
                                              recorrer_tablero(T0,X1,Y,Dir,Tam,Acum1),!.
recorrer_tablero(T0, X, Yc, Dir, Tam, Acum):- Dir=v,	
                                              Acum < Tam,
                                              Acum1 is Acum+1, 
                                              hay_elemento(T0,X,Yc,g),
                                              Y1 is Yc+1,
                                              recorrer_tablero(T0,X,Y1,Dir,Tam,Acum1),!.

%% hundir_barco(+Columna_Inicial, +Fila_Inicial, +Direccion, 
%               +Tamano, +Tablero_Inicial, -Tablero)
%  Retorna un Tablero con el barco hundido.

hundir_barco(Ci, Fi, D, Tam, T0, T):- barco(Ci,Fi,D,Tam),
                                      dibujar_barco_hundido(Ci,Fi,D,Tam,T0,T).

%% dibujar_barco_hundido(+Columna_Inicial, +Fila_Inicial, +Direccion, 
%                        +Tamano, +Tablero_Inicial, -Tablero)
%  Dibuja un Tablero con el barco hundido.

dibujar_barco_hundido(_, _, v, Tam, T, T):- Tam =< 0,!.
dibujar_barco_hundido(Ci, Fi, h, Tam, T0, T):- obtener_sublista(T0,Fi,Fila),
                                               cambiar_edo_completo(Fila,Ci,Tam,Fila_Camb,0),
                                               obtener_inicio(T0,Fi,L1),
                                               append(L1,[Fila_Camb],L2),
                                               length(L2,Tam_Aux),
                                               length(T0,Tam_Tab),
                                               Aux is Tam_Tab-Tam_Aux,
                                               obtener_fin(T0,Aux,L3),
                                               append(L2,L3,T).
dibujar_barco_hundido(Ci, Fi, v, Tam, T0, T):- Tam > 0,
                                               obtener_sublista(T0,Fi,Fila),
                                               cambiar_edo(Fila,Ci,h,Fila_Camb),
                                               obtener_inicio(T0,Fi,L1),
                                               append(L1,[Fila_Camb],L2),
                                               length(L2,Tam_Aux),
                                               length(T0,Tam_Tab),
                                               Aux is Tam_Tab-Tam_Aux,
                                               obtener_fin(T0,Aux,L3),
                                               append(L2,L3,T1),
                                               Y is Fi+1,
                                               Tam1 is Tam-1,
                                               dibujar_barco_hundido(Ci,Y,v,Tam1,T1,T).

%% cambiar_edo_completo(+Fila, +Posicion, +Tamano, 
%                       -Fila_Cambiada, +Acumulador)
%  Retorna una fila con un barco hundido.

cambiar_edo_completo(Fila, _, Tam, Fila, Acum):- Acum >= Tam,!.
cambiar_edo_completo(Fila, P, Tam, Fila_Camb, Acum):- Acum < Tam,
                                                      cambiar_edo(Fila,P,h,FilaC),
                                                      Acum1 is Acum+1,
                                                      X is P+1,
                                                      cambiar_edo_completo(FilaC,X,Tam,Fila_Camb,Acum1).

%% mostrar_solucion(+T)
%  Muestra la secuencia de tableros que corresponden 
%  a una solucion factible.

mostrar_solucion([]):-!.
mostrar_solucion([H|R]):- mostrar_tablero(H), nl,
                          mostrar_solucion(R).

%% llenar_ataque_secuencial(+T0,+F,+C,+Ax,+Ay)
%  Construye la solucion a partir de las posiciones
%  atacadas, que conforman la solucion factible,
%  muestra los tableros paso a paso.

llenar_ataque_secuencial(T0,F,C,Ax,Ay):- Ax < C,
                                         Ay >= F,!.
											  
llenar_ataque_secuencial(T0,F,C,Ax,Ay):- Ax < C,
                                         Ay < F,
										 ataque(Ax,Ay),
										 ataque(T0, T, F, Ax, Ay),
										 retract(ataque(Ax,Ay)),%OJO
										 mostrar_tablero(T),nl,
										 X1 is Ax+1,
										 llenar_ataque_secuencial(T,F,C,X1,Ay).
										 
llenar_ataque_secuencial(T0,F,C,Ax,Ay):- Ax < C,
                                         Ay < F,
										 not(ataque(Ax,Ay)),
										 X1 is Ax+1,
										 llenar_ataque_secuencial(T0,F,C,X1,Ay).
										 
llenar_ataque_secuencial(T0,F,C,Ax,Ay):- Ax >= C,
                                         Ay < F,
                                         Y1 is Ay+1,
										 llenar_ataque_secuencial(T0,F,C,0,Y1).
											  
											  
											  
											  
											  